package com.coder.books.thinkinjava.concurrency;


import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import static com.coder.util.Print.println;

/**
 * 汽车组装
 *
 * @author yuhushuan
 * @date 2020/9/6 16:43
 */


class Car {

    private final int id;
    private boolean engine = false, driveTrain = false, wheels = false;

    public Car(int idn) {
        id = idn;
    }

    public Car() {
        id = -1;
    }

    public synchronized int getId() {
        return id;
    }

    public synchronized void addEngine() {
        engine = true;
    }

    public synchronized void addDriveTrain() {
        driveTrain = true;
    }

    public synchronized void addWheels() {
        wheels = true;
    }

    @Override
    public synchronized String toString() {
        return "Car " + id + " [ engine: " + engine
                + " driveTrain: " + driveTrain
                + " wheels: " + wheels + "]";
    }
}

class CarQueue extends LinkedBlockingQueue<Car> {
}

// 底盘制造器
class ChassisBuilder implements Runnable {
    private CarQueue carQueue;
    private int counter = 0;

    public ChassisBuilder(CarQueue cq) {
        this.carQueue = cq;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(500);
                // 制作地盘
                Car c = new Car(counter++);
                println("ChassisBuilder created " + c);
                // 插入队列
                carQueue.put(c);
            }
        } catch (InterruptedException e) {
            println("Interrupted: ChassisBuilder");
        }
        println("ChassisBuilder off");
    }
}

// 组装工序
class Assembler implements Runnable {
    private CarQueue chassisQueue, finishingQueue;
    private Car car;
    private CyclicBarrier barrier = new CyclicBarrier(4);
    private RobotPool robotPool;

    public Assembler(CarQueue cq, CarQueue fq, RobotPool rp) {
        chassisQueue = cq;
        finishingQueue = fq;
        robotPool = rp;
    }

    public Car car() {
        return car;
    }

    public CyclicBarrier barrier() {
        return barrier;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                // 底盘可用前阻塞
                car = chassisQueue.take();
                // 雇用机器人执行工作
                robotPool.hire(EngineRobot.class, this);
                robotPool.hire(DriveTrainRobot.class, this);
                robotPool.hire(WheelRobot.class, this);
                barrier.await();

                // 将汽车放入 FinishingQueue 进行进一步的工作
                finishingQueue.put(car);
            }
        } catch (InterruptedException e) {
            println("Exiting Assembler via interrupt");
        } catch (BrokenBarrierException e) {
            // 我们需要知道原因
            throw new RuntimeException(e);
        }
        println("Assembler off");
    }
}

// 记录
class Reporter implements Runnable {
    private CarQueue carQueue;

    public Reporter(CarQueue cq) {
        carQueue = cq;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                println(carQueue.take() + "\n");
            }
        } catch (InterruptedException e) {
            println("Exiting Reporter via interrupt");
        }
        println("Reporter off");
    }
}

// 机器人
abstract class Robot implements Runnable {
    private RobotPool pool;

    public Robot(RobotPool p) {
        pool = p;
    }

    protected Assembler assembler;

    public Robot assignAssembler(Assembler assembler) {
        this.assembler = assembler;
        return this;
    }

    private boolean engage = false;

    // 雇用
    public synchronized void engage() {
        engage = true;
        notifyAll();
    }

    // run() 方法的一部分，不同机器人有不同实现
    abstract protected void performService();


    @Override
    public void run() {
        try {
            powerDown();// 等到需要
            while (!Thread.interrupted()) {
                performService();
                assembler.barrier().await();

                // 我们已经完成了这项工作
                powerDown();
            }

        } catch (InterruptedException e) {
            println("Exiting " + this + " via interrupt");
        } catch (BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
        println(this + " off");
    }

    private synchronized void powerDown() throws InterruptedException {
        engage = false;
        assembler = null;// 与组装器断开连接

        // 让自己回到可用的池中
        pool.release(this);

        while (!engage) {// powerDown
            wait();
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }
}

// 发动机机器人
class EngineRobot extends Robot {

    public EngineRobot(RobotPool p) {
        super(p);
    }

    @Override
    protected void performService() {
        println(this + " installing Engine");
        assembler.car().addEngine();
    }
}

// 传动系统机器人
class DriveTrainRobot extends Robot {

    public DriveTrainRobot(RobotPool p) {
        super(p);
    }

    @Override
    protected void performService() {
        println(this + " installing DriveTrain");
        assembler.car().addDriveTrain();
    }
}

// 车轮机器人
class WheelRobot extends Robot {

    public WheelRobot(RobotPool p) {
        super(p);
    }

    @Override
    protected void performService() {
        println(this + " installing Wheels");
        assembler.car().addWheels();
    }
}

//机器人池
class RobotPool {

    private Set<Robot> pool = new HashSet<>();

    public synchronized void add(Robot robot) {
        pool.add(robot);
        notifyAll();
    }

    public synchronized void hire(Class<? extends Robot> robotType, Assembler d) throws InterruptedException {
        for (Robot robot : pool) {
            if (robot.getClass().equals(robotType)) {
                pool.remove(robot);
                robot.assignAssembler(d);
                robot.engage();
                return;
            }
        }
        wait();// 无可用
        hire(robotType, d);// 递归，重试
    }

    public synchronized void release(Robot robot) {
        add(robot);
    }
}

public class CarBuilder {
    public static void main(String[] args) throws InterruptedException {
        CarQueue chassisQueue = new CarQueue(),
                finishingQueue = new CarQueue();
        ExecutorService exec = Executors.newCachedThreadPool();
        RobotPool robotPool = new RobotPool();

        exec.execute(new EngineRobot(robotPool));
        exec.execute(new DriveTrainRobot(robotPool));
        exec.execute(new WheelRobot(robotPool));
        exec.execute(new Assembler(chassisQueue, finishingQueue, robotPool));
        exec.execute(new Reporter(finishingQueue));
        exec.execute(new ChassisBuilder(chassisQueue));
        TimeUnit.SECONDS.sleep(7);

        println("\n===============================");
        exec.shutdownNow();
    }

}
